﻿namespace wToggle.Platform
{
    using System;
    using System.Net.Http;
    using System.Net.Http.Headers;
    using System.Threading.Tasks;

    using wToggle.Platform.Data;
    using wToggle.Platform.Interfaces;

    using XPlatUtils;

    public class TogglAuth : ITogglAuth
    {
        private const string TogglBaseAddress = "https://www.toggl.com/api/v8/";

        private bool isAuthenticated;

        private string username;

        private string pass;

        private TogglAuth()
        {
        }

        internal static TogglAuth Create()
        {
            return new TogglAuth();
        }

        public async Task<AuthStatus> Authenticate(string email, string password)
        {
            if (this.isAuthenticated)
            {
                return AuthStatus.Success;
            }

            var status = await this.AuthenticateCore(email, password).ConfigureAwait(false);

            if (status == AuthStatus.Success)
            {
                this.username = email;
                this.pass = password;
                this.isAuthenticated = true;
            }

            return status;
        }

        public async Task<AuthStatus> Authenticate(string apiKey)
        {
            if (this.isAuthenticated)
            {
                return AuthStatus.Success;
            }

            var status = await this.AuthenticateCore(apiKey, "api_token").ConfigureAwait(false);

            if (status == AuthStatus.Success)
            {
                this.username = apiKey;
                this.pass = "api_token";
                this.isAuthenticated = true;
            }

            return status;
        }

        public bool IsAuthenticated
        {
            get
            {
                return this.isAuthenticated;
            }
        }

        public HttpClient GetPreauthenticatedClient()
        {
            if (!this.isAuthenticated)
            {
                throw new InvalidOperationException("Authentication failed");
            }

            return this.CreateAuthenticatedClient(this.username, this.pass);
        }

        public async Task LogOff()
        {
            if (!this.IsAuthenticated)
            {
                return;
            }

            this.username = null;
            this.pass = null;
            this.isAuthenticated = false;
            await CacheLayer.Purge();
        }

        private async Task<AuthStatus> AuthenticateCore(string username, string password)
        { 
            if (Toggly.HasInternetAccess())
            {
                try
                {
                    using (var client = this.CreateAuthenticatedClient(username, password))
                    {
                        var response = await client.GetJsonAsync<AuthResponse>("me").ConfigureAwait(false);

                        if (response == null)
                        {
                            return AuthStatus.Failure;
                        }

                        await CacheLayer.StoreEntity(response.Data);

                        return AuthStatus.Success;
                    }
                }
                catch (HttpRequestException e)
                {
                    return AuthStatus.Failure;
                }
            }
            else
            {
                var settings = ServiceContainer.Resolve<IApplicationSettings>();
                var data = CacheLayer.LoadEntity<UserData>(0);

                if (data == null)
                {
                    return settings.HasLogin() ? AuthStatus.Success : AuthStatus.Failure;
                }
                else
                {
                    if (settings.Password == "api_token")
                    {
                        return settings.Username == data.ApiToken ? AuthStatus.Success : AuthStatus.Failure;
                    }
                    else
                    {
                        return settings.Username == data.Email ? AuthStatus.Success : AuthStatus.Failure;
                    }
                }
            } 
        }

        private HttpClient CreateAuthenticatedClient(string username, string password)
        {
            var client = new HttpClient { BaseAddress = new Uri(TogglBaseAddress, UriKind.Absolute) };
            var byteArray = string.Format("{0}:{1}", username, password).ToASCII();
            var header = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(byteArray));
            client.DefaultRequestHeaders.Authorization = header;

            return client;
        }
    }
}
